package com.smart.thrift.config.server;


import cn.hutool.core.lang.Pair;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.smart.thrift.constants.ThriftConstants;
import com.smart.thrift.discoveryandbalance.discovery.Registry;
import com.smart.thrift.discoveryandbalance.discovery.ThriftServerNode;
import com.smart.thrift.discoveryandbalance.discovery.consul.ThriftConsulServerNode;
import com.smart.thrift.discoveryandbalance.discovery.zookeeper.ThriftZookeeperServerNode;
import com.smart.thrift.model.common.ThriftServerProperties;
import com.smart.thrift.model.server.IThriftServer;
import com.smart.thrift.model.server.ServerBuilder;
import com.smart.thrift.model.wrapper.ThriftServiceWrapper;
import com.smart.thrift.util.IPUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.thrift.transport.TTransportException;

import java.io.IOException;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

@Slf4j
public class ThriftServerManager {

    private static final Map<String, ServerBuilder> SERVER_BUILDER_MAP = MapUtil.of(
            Pair.of(ThriftConstants.ServiceModel.SERVICE_MODEL_SIMPLE, ServerBuilder.TSimpleServerSingletonBuilder.getInstance()),
            Pair.of(ThriftConstants.ServiceModel.SERVICE_MODEL_NON_BLOCKING, ServerBuilder.TNonBlockingServerSingletonBuilder.getInstance()),
            Pair.of(ThriftConstants.ServiceModel.SERVICE_MODEL_THREAD_POOL, ServerBuilder.TThreadPoolServerSingletonBuilder.getInstance()),
            Pair.of(ThriftConstants.ServiceModel.SERVICE_MODEL_THREADED_SELECTOR, ServerBuilder.TThreadedSelectorServerSingletonBuilder.getInstance()),
            Pair.of(ThriftConstants.ServiceModel.SERVICE_MODEL_HS_HA, ServerBuilder.THsHaServerSingletonBuilder.getInstance())
    );

    /**
     * 对外服务名
     */
    protected String serverName;
    protected String serverId;
    private Registry<?> registry;
    private final Lock thriftServerLock = new ReentrantLock();
    private ThriftServerProperties properties;
    private List<ThriftServiceWrapper> serviceWrappers;
    private volatile IThriftServer thriftServer;

    public IThriftServer buildServer() throws TTransportException, IOException {
        if (Objects.isNull(thriftServer)) {
            thriftServerLock.lock();
            try {
                if (Objects.isNull(thriftServer)) {
                    String serviceModel = properties.getServiceModel();
                    log.info("Build thrift server from " + serviceModel);
                    thriftServer = SERVER_BUILDER_MAP.containsKey(serviceModel) ?
                            SERVER_BUILDER_MAP.get(serviceModel).buildThriftServer(properties, serviceWrappers) :
                            buildDefaultThriftServer();
                    thriftServer.setServerId(serverId);
                    thriftServer.setServerName(serverName);
                    thriftServer.setThriftServerNode(buildThriftServerNode());
                    thriftServer.setServiceWrappers(serviceWrappers);
                    ThriftServerProperties.ThriftServerDiscoveryProperties discovery = properties.getDiscovery();
                    if (discovery != null && discovery.getEnabled()){
                        thriftServer.setRegistry(registry);
                    }
                }
            } finally {
                thriftServerLock.unlock();
            }
        }
        return thriftServer;
    }

    public synchronized IThriftServer buildDefaultThriftServer() throws TTransportException, IOException {
        return ServerBuilder.THsHaServerSingletonBuilder.getInstance().buildThriftServer(properties, serviceWrappers);
    }

    public synchronized ThriftServerNode buildThriftServerNode() throws SocketException, UnknownHostException {
        ThriftServerProperties.ThriftServerDiscoveryProperties discovery = properties.getDiscovery();
        ThriftServerNode serverNode = new ThriftServerNode();
        if (discovery != null && discovery.getEnabled()){
            if (ThriftConstants.DiscoveryType.ZOOKEEPER.equals(discovery.getDiscoveryType())){
                serverNode = new ThriftZookeeperServerNode();
                serverNode.setHost(StrUtil.isNotBlank(properties.getHost()) ? properties.getHost(): IPUtil.getLocalAddress(null));
                serverNode.setPort(properties.getPort());
                serverNode.setTags(properties.getTags());
            } else if (ThriftConstants.DiscoveryType.CONSUL.equals(discovery.getDiscoveryType())){
                serverNode = new ThriftConsulServerNode();
                serverNode.setHost(StrUtil.isNotBlank(properties.getHost()) ? properties.getHost(): IPUtil.getLocalAddress(null));
                serverNode.setPort(properties.getPort());
                serverNode.setTags(properties.getTags());
            }
        }
        return serverNode;
    }

    public String getServerName() {
        return serverName;
    }

    public void setServerName(String serverName) {
        this.serverName = serverName;
    }

    public String getServerId() {
        return serverId;
    }

    public void setServerId(String serverId) {
        this.serverId = serverId;
    }

    public Registry getRegistry() {
        return registry;
    }

    public void setRegistry(Registry registry) {
        this.registry = registry;
    }

    public ThriftServerProperties getProperties() {
        return properties;
    }

    public void setProperties(ThriftServerProperties properties) {
        this.properties = properties;
    }

    public List<ThriftServiceWrapper> getServiceWrappers() {
        return serviceWrappers;
    }

    public void setServiceWrappers(List<ThriftServiceWrapper> serviceWrappers) {
        this.serviceWrappers = serviceWrappers;
    }
}
